/*
 * Copyright 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.yangfei.SportAssistant;

import com.yangfei.SportAssistant.content.SportAssistantProviderUtils;
import com.yangfei.SportAssistant.content.SportAssistantProviderUtils.Factory;
import com.yangfei.SportAssistant.content.Track;
import com.yangfei.SportAssistant.content.TrackDataHub;
import com.yangfei.SportAssistant.content.TrackDataHub.ListenerDataType;
import com.yangfei.SportAssistant.content.TrackDataListener;
import com.yangfei.SportAssistant.content.TrackPointsColumns;
import com.yangfei.SportAssistant.content.Waypoint;
import com.yangfei.SportAssistant.mapProvider.BitmapSegmentsOverlay;
import com.yangfei.SportAssistant.mapProvider.GeoPointYf;
import com.yangfei.SportAssistant.mapProvider.LoggerMap;
import com.yangfei.SportAssistant.mapProvider.SegmentRendering;
import com.yangfei.SportAssistant.stats.TripStatistics;
import com.yangfei.SportAssistant.ui.EActivityStats;
import com.yangfei.SportAssistant.ui.InfoCtrlGroup;
import com.yangfei.SportAssistant.util.Constants;
import com.yangfei.SportAssistant.util.GeoRect;
import com.yangfei.SportAssistant.util.LocationUtils;
import com.yangfei.SportAssistant.util.UnitConversions;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.graphics.Canvas;
import android.location.Location;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import java.text.NumberFormat;
import java.util.EnumSet;

/**
 * A fragment to display map to the user.
 * 
 * @author Leif Hendrik Wilden
 * @author Rodrigo Damazio
 */
public class MapHelper implements View.OnTouchListener, View.OnClickListener, TrackDataListener
{
	private static final String TAG = MapHelper.class.getSimpleName();

	private static final String KEY_CURRENT_LOCATION = "currentLocation";
	private static final String KEY_KEEP_MY_LOCATION_VISIBLE = "keepMyLocationVisible";

	private TrackDataHub trackDataHub = null;

	// True to keep my location visible.
	private boolean keepMyLocationVisible;

	// True to zoom to my location. Only apply when keepMyLocationVisible is true.
	private boolean zoomToMyLocation;

	// The track id of the waypoint to show.
	private long waypointTrackId;

	// The waypoint id to show
	private long waypointId;

	// The current selected track id. Set in onSelectedTrackChanged.
	private long currentSelectedTrackId;

	// The current location. Set in onCurrentLocationChanged.
	private Location currentLocation = null;

	// UI elements
	private RelativeLayout screen;
	private LinearLayout messagePane;
	private TextView messageText;
	private LinearLayout busyPane;

	private UiUpdateThread uiUpdateThread;

	private SharedPreferences sharedPreferences;

	boolean metricUnits = true;

	private Spinner activitySelect = null;

	final int KBUTTONNUMBER = 4;
	InfoCtrlGroup mInfoCtrlGroup[] = new InfoCtrlGroup[KBUTTONNUMBER];

	private StatsUtilities statsUtilities;
	private long startTime = -1L;

	private ImageButton myPositionBtn;
	private ImageButton startRecordBtn;
	private ImageButton fullScreenMapBtn;

	private boolean IsRecoding = false;

	private LoggerMap mMap;
	private Handler mHandler;
	private BitmapSegmentsOverlay mOverlay;
	private SegmentRendering mSegmentOverlay;
	private enum EMapViewState
	{
		MapViewNormal, MapViewMapFullScreen
	};

	EMapViewState mState = EMapViewState.MapViewNormal;

	boolean isAutoFollowing = false;

	private enum EActivityType
	{
		activity_walking(0), activity_running(1), activity_cycling(2), activity_hiking(3), activity_golf(4),
		activity_mountainBiking(5);

		private final int index;

		EActivityType(int index)
		{
			this.index = index;
		}

		public int index()
		{
			return index;
		}

		public static EActivityType getValueFromInt(int index)
		{
			for (EActivityType s : EActivityType.values())
			{
				if (s.index == index)
					return s;
			}
			return activity_walking;
		}

		public int getNum()
		{
			return EActivityType.values().length;
		}
	};

	private EActivityType mActivityType = EActivityType.activity_walking;
	
	public LoggerMap getmMap()
	{
		return mMap;
	}

	public MapHelper(LoggerMap map, Bundle savedInstanceState)
	{
		Log.d(TAG, "onCreate");
		mMap = map;
		if (savedInstanceState != null)
		{
			keepMyLocationVisible = savedInstanceState.getBoolean(KEY_KEEP_MY_LOCATION_VISIBLE, false);
			currentLocation = (Location) savedInstanceState.getParcelable(KEY_CURRENT_LOCATION);
			if (!LocationUtils.isValidLocation(currentLocation))
				currentLocation = null;
			if (currentLocation != null)
			{
				updateCurrentLocation();
			}
		}
		mMap.getActivity().requestWindowFeature(Window.FEATURE_NO_TITLE);
	}
	
	public void Init()
	{
		statsUtilities = new StatsUtilities(mMap.getActivity());

		screen = (RelativeLayout) mMap.getActivity().findViewById(R.id.mapScreen);

		messagePane = (LinearLayout) mMap.getActivity().findViewById(R.id.messagepane);
		messageText = (TextView) mMap.getActivity().findViewById(R.id.messagetext);
		busyPane = (LinearLayout) mMap.getActivity().findViewById(R.id.busypane);

		sharedPreferences = mMap.getActivity().getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
		if (sharedPreferences != null)
		{
			metricUnits = sharedPreferences.getBoolean(mMap.getActivity().getString(R.string.metric_units_key), true);
			sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
		}

		trackDataHub = ((SportAssistantApplication) mMap.getActivity().getApplication()).getTrackDataHub();

		// init baidu mapview
		mMap.getMapView().requestFocus();
		mMap.getMapView().setOnTouchListener(this);
		
		mHandler = new Handler();
		mOverlay = new BitmapSegmentsOverlay(mMap, mHandler);
		mSegmentOverlay = new SegmentRendering(mMap.getActivity());
		mOverlay.addSegment(mSegmentOverlay);
		mMap.addOverlay(mOverlay);

		InitUI();
	}

	public void onDestroy()
	{
		Log.d(TAG, "onDestroy");
	}

	public void onSaveInstanceState(Bundle outState)
	{
		Log.d(TAG, "onSaveInstanceState");
		outState.putBoolean(KEY_KEEP_MY_LOCATION_VISIBLE, keepMyLocationVisible);
		if (currentLocation != null)
		{
			outState.putParcelable(KEY_CURRENT_LOCATION, currentLocation);
		}
	}

	public void onPause()
	{
		Log.d(TAG, "onPause");
		pauseTrackDataHub();
		StopUIUpdate();
	}

	public void onResume()
	{
		Log.d(TAG, "onResume");
		resumeTrackDataHub();
		setTheGoAndStopBtn();
		if (MainTabActivity.getInstance() != null)
		{
			IsRecoding = MainTabActivity.getInstance().isRecording();
			if (IsRecoding)
				StartUIUpdate();
		}
	}

	/**
	 * Shows my location.
	 */
	public boolean showMyLocation()
	{
		Log.d(TAG, "showMyLocation");
		// updateTrackDataHub();
		keepMyLocationVisible = true;
		zoomToMyLocation = true;
		if (currentLocation != null)
		{
			updateCurrentLocation();
			return true;
		}
		else
		{
			Toast.makeText(mMap.getActivity(), R.string.waiting_for_gps, Toast.LENGTH_LONG).show();
			return false;
		}
	}

	/**
	 * Shows the waypoint.
	 * 
	 * @param id
	 *          the waypoint id
	 */
	public void showWaypoint(long id)
	{
		Log.d(TAG, "showWaypoint");
		SportAssistantProviderUtils SportAssistantProviderUtils = Factory.get(mMap.getActivity());
		Waypoint waypoint = SportAssistantProviderUtils.getWaypoint(id);
		if (waypoint != null && waypoint.getLocation() != null)
		{
			keepMyLocationVisible = false;
			GeoPointYf center = new GeoPointYf((int) (waypoint.getLocation().getLatitude() * 1E6), (int) (waypoint.getLocation().getLongitude() * 1E6));
			mMap.setCenter(center);
			mMap.setZoom(mMap.getMaxZoomLevel());
			mMap.getMapView().invalidate();
		}
	}

	/**
	 * Shows the waypoint.
	 * 
	 * @param trackId
	 *          the track id
	 * @param id
	 *          the waypoint id
	 */
	public void showWaypoint(long trackId, long id)
	{
		Log.d(TAG, "showWaypoint");
		/*
		 * Synchronize to prevent race condition in changing waypointTrackId and
		 * waypointId variables.
		 */
		synchronized (this)
		{
			if (trackId == currentSelectedTrackId)
			{
				showWaypoint(id);
				waypointTrackId = -1L;
				waypointId = -1L;
				return;
			}
			waypointTrackId = trackId;
			waypointId = id;
		}
	}

	/**
	 * Returns true if in satellite mode.
	 */
	public boolean isSatelliteView()
	{
		Log.d(TAG, "isSatelliteView");
		return mMap.isSatelliteView();
	}

	/**
	 * Sets the satellite mode
	 * 
	 * @param enabled
	 *          true for satellite mode, false for map mode
	 */
	public void setSatelliteView(boolean enabled)
	{
		Log.d(TAG, "setSatelliteView");
		mMap.setSatelliteView(enabled);
	}

	@Override
	public boolean onTouch(View view, MotionEvent event)
	{
		if (currentLocation == null)
			return false;

		// Log.d(TAG, "onTouch");
		if (keepMyLocationVisible && event.getAction() == MotionEvent.ACTION_MOVE)
		{
			if (!isVisible(currentLocation))
			{
				/*
				 * Only set to false when no longer visible. Thus can keep showing the
				 * current location with the next location update.
				 */
				keepMyLocationVisible = false;
			}
		}
		return false;
	}

	@Override
	public void onClick(View v)
	{
		Log.d(TAG, "onClick");
		if (v == messagePane)
		{
			mMap.getActivity().startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
		}

		if (v == startRecordBtn)
		{
			IsRecoding = MainTabActivity.getInstance().isRecording();
			if (!IsRecoding)
			{
				StartUIUpdate();
				startAutoFollowSilent();
				MainTabActivity.getInstance().startRecording();
			}
			else
			{
				StopUIUpdate();
				cancelAutoFollowSilent();
				MainTabActivity.getInstance().stopRecording();
			}
		}
		if (v == myPositionBtn)
		{
			if (showMyLocation() == true)
			{
				if (isAutoFollowing == false)
					startAutoFollow();
				else
					cancelAutoFollow();
			}
		}
		if (v == fullScreenMapBtn)
		{
			switch (mState)
			{
				case MapViewNormal:
				{
					setFullMap(true);
				}
					break;
				case MapViewMapFullScreen:
				{
					setFullMap(false);
				}
			}
		}
	}

	public boolean onKeyDown(int keyCode, KeyEvent event)
	{
		return false;
	}

	@Override
	public void onProviderStateChange(ProviderState state)
	{
		Log.d(TAG, "onProviderStateChange");
		final int messageId;
		final boolean isGpsDisabled;
		switch (state)
		{
			case DISABLED:
				messageId = R.string.gps_need_to_enable;
				isGpsDisabled = true;
				break;
			case NO_FIX:
			case BAD_FIX:
				messageId = R.string.gps_wait_for_fix;
				isGpsDisabled = false;
				break;
			case GOOD_FIX:
				messageId = -1;
				isGpsDisabled = false;
				break;
			default:
				throw new IllegalArgumentException("Unexpected state: " + state);
		}

		mMap.getActivity().runOnUiThread(new Runnable()
		{
			@Override
			public void run()
			{
				if (messageId != -1)
				{
/*					messageText.setText(messageId);
					messagePane.setVisibility(View.VISIBLE);

					if (isGpsDisabled)
					{
						Toast.makeText(mMap.getActivity(), R.string.gps_not_found, Toast.LENGTH_LONG).show();

						// Click to show the location source settings
						messagePane.setOnClickListener(MapHelper.this);
					}
					else
					{
						messagePane.setOnClickListener(null);
					}*/
				}
				else
				{
					messagePane.setVisibility(View.GONE);
				}
				screen.requestLayout();
			}
		});
	}

	@Override
	public void onCurrentLocationChanged(final Location location)
	{
		Log.d(TAG, "onCurrentLocationChanged");
		if (location == null)
			return;
	}

//notify compass update
	@Override
	public void onCurrentHeadingChanged(double heading)
	{
		// Log.d(TAG, "onCurrentHeadingChanged");
		if (mSegmentOverlay.setHeading((float) heading))
		{
			Log.d("drawTrack", "onCurrentHeadingChanged::mapView.postInvalidate()");
			mMap.postInvalidate();
		}
	}

	@Override
	public void onSelectedTrackChanged(final Track track, final boolean isRecording)
	{
		Log.d(TAG, "onSelectedTrackChanged");
		if (track == null)
			return;

		currentSelectedTrackId = track.getId();
		mMap.getActivity().runOnUiThread(new Runnable()
		{
			@Override
			public void run()
			{
				boolean hasTrack = track != null;
				mSegmentOverlay.setTrackDrawingEnabled(hasTrack);

				if (hasTrack)
				{
					busyPane.setVisibility(View.VISIBLE);

					synchronized (this)
					{
						/*
						 * Synchronize to prevent race condition in changing waypointTrackId
						 * and waypointId variables.
						 */
						updateMap(track);
					}

					UpdateTrackInfo(track);
					mSegmentOverlay.setShowEndMarker(!isRecording);
					busyPane.setVisibility(View.GONE);
					mMap.getMapView().invalidate();
				}
			}
		});
	}

	private void StartUIUpdate()
	{
		if (uiUpdateThread == null)
		{
			Log.d(TAG, "StartUIUpdate");
			uiUpdateThread = new UiUpdateThread();
			uiUpdateThread.start();
		}
	}

	private void StopUIUpdate()
	{
		if (uiUpdateThread != null)
		{
			Log.d(TAG, "StopUIUpdate");
			uiUpdateThread.interrupt();
			uiUpdateThread = null;
		}
	}

	@Override
	public void onTrackUpdated(final Track track)
	{
		Log.d(TAG, "onTrackUpdated");
		// We don't care.
		mMap.getActivity().runOnUiThread(new Runnable()
		{
			@Override
			public void run()
			{
				if (track == null || track.getStatistics() == null)
				{
					setLocationUnknown();
					return;
				}

				startTime = track.getStatistics().getStartTime();
				if (!isRecording())
				{
					setLocationUnknown();
				}
				UpdateTrackInfo(track);
			}
		});
	}

	private void setLocationUnknown()
	{
		Log.d(TAG, "setLocationUnknown");
		for (int i = 0; i < KBUTTONNUMBER; i++)
		{
			if (mInfoCtrlGroup[i] != null)
				statsUtilities.setUnknown(mInfoCtrlGroup[i].infoText);
		}
	}

	private void UpdateGpsAcc()
	{
		TextView gpsAcc = (TextView) mMap.getActivity().findViewById(R.id.TableLayout0_TextGpsData);
		if(gpsAcc==null)
			return;
		if (currentLocation == null || isRecording() == false)
		{
			gpsAcc.setText(R.string.value_unknown);
			return;
		}

		NumberFormat ALTITUDE_FORMAT = NumberFormat.getIntegerInstance();
		float gpsAccValue = currentLocation.getAccuracy();
		if (!Double.isNaN(gpsAccValue) && !Double.isInfinite(gpsAccValue))
		{
			String gpsAccUnit;
			gpsAccUnit = ALTITUDE_FORMAT.format(gpsAccValue);
			gpsAccUnit += "(" + mMap.getActivity().getString(R.string.unit_meter) + ")";
			gpsAcc.setText(gpsAccUnit);
		}
		else
		{
			gpsAcc.setText(R.string.value_unknown);
		}
	}

	private synchronized boolean isRecording()
	{
		return trackDataHub != null && trackDataHub.isRecordingSelected();
	}

	@Override
	public void clearTrackPoints()
	{
		mSegmentOverlay.clearPoints();
	}

	@Override
	public void onNewTrackPoint(final Location location)
	{
		Log.d(TAG, "onNewTrackPoint");
		if (LocationUtils.isValidLocation(location))
		{
			Bundle chinaCoord = location.getExtras();
			if (chinaCoord != null)
			{
				double chinaLong = chinaCoord.getDouble(TrackPointsColumns.LONGITUDECHINA);
				double chinaLat = chinaCoord.getDouble(TrackPointsColumns.LATITUDECHINA);

				location.setLatitude(chinaLat);
				location.setLongitude(chinaLong);

				if (LocationUtils.isValidLocation(location))
				{
					mSegmentOverlay.addLocation(location);
					Log.w("updateCurrentLocation", "onNewTrackPoint " + " Latitude = " + location.getLatitude() + " Longitude = "
							+ location.getLongitude());
					currentLocation = location;
				}
				else
				{
					return;
				}
			}

			// for stats
			if (isRecording())
			{
				mMap.getActivity().runOnUiThread(new Runnable()
				{
					@Override
					public void run()
					{
						if (location != null)
						{
							UpdateLocationInfo(location);
						}
						else
						{
							setLocationUnknown();
						}
					}
				});
			}
		}
	}

	@Override
	public void onSampledOutTrackPoint(Location loc)
	{
		// We don't care.
	}

	@Override
	public void onSegmentSplit()
	{
		Log.d(TAG, "onSegmentSplit");
		mSegmentOverlay.addSegmentSplit();
	}

	@Override
	public void onNewTrackPointsDone()
	{
		Log.d(TAG, "onNewTrackPointsDone");
		Log.d("drawTrack", "onNewTrackPointsDone::mapView.postInvalidate()");
		mMap.postInvalidate();
	}

	@Override
	public void clearWaypoints()
	{
		Log.d(TAG, "clearWaypoints");
		mSegmentOverlay.clearWaypoints();
	}

	@Override
	public void onNewWaypoint(Waypoint waypoint)
	{
		Log.d(TAG, "onNewWaypoint");
		if (waypoint != null && LocationUtils.isValidLocation(waypoint.getLocation()))
		{
			// TODO: Optimize locking inside addWaypoint
			mSegmentOverlay.addWaypoint(waypoint);
		}
	}

	@Override
	public void onNewWaypointsDone()
	{
		Log.d(TAG, "onNewWaypointsDone");
		Log.d("drawTrack", "onNewWaypointsDone::mapView.postInvalidate()");
		mMap.postInvalidate();
	}

	@Override
	public boolean onUnitsChanged(boolean metric)
	{
		Log.d(TAG, "onUnitsChanged");
		if (statsUtilities.isMetricUnits() == metric)
		{
			return false;
		}
		statsUtilities.setMetricUnits(metric);
		mMap.getActivity().runOnUiThread(new Runnable()
		{
			@Override
			public void run()
			{
				UpdateInfoUnit();
			}
		});
		return true;
	}

	@Override
	public boolean onReportSpeedChanged(boolean reportSpeed)
	{
		Log.d(TAG, "onReportSpeedChanged");
		// We don't care.
		return false;
	}

	private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener()
	{
		@Override
		public void onSharedPreferenceChanged(SharedPreferences preferences, String key)
		{
			Log.d(TAG, "onSharedPreferenceChanged");
			metricUnits = sharedPreferences.getBoolean(mMap.getActivity().getString(R.string.metric_units_key), true);
		}
	};

	/**
	 * Resumes the trackDataHub. Needs to be synchronized because trackDataHub can
	 * be accessed by multiple threads.
	 */
	private synchronized void resumeTrackDataHub()
	{
		Log.d(TAG, "resumeTrackDataHub");
		if (trackDataHub == null)
			return;

		trackDataHub.registerTrackDataListener(this, EnumSet.of(ListenerDataType.SELECTED_TRACK_CHANGED, ListenerDataType.WAYPOINT_UPDATES, ListenerDataType.POINT_UPDATES, ListenerDataType.TRACK_UPDATES, ListenerDataType.LOCATION_UPDATES, ListenerDataType.COMPASS_UPDATES));
	}

	/**
	 * Pauses the trackDataHub. Needs to be synchronized because trackDataHub can
	 * be accessed by multiple threads.
	 */
	private synchronized void pauseTrackDataHub()
	{
		Log.d(TAG, "pauseTrackDataHub");
		if (trackDataHub == null)
			return;

		trackDataHub.unregisterTrackDataListener(this);
	}

	/**
	 * Updates the trackDataHub. Needs to be synchronized because trackDataHub can
	 * be accessed by multiple threads.
	 */
	private synchronized void updateTrackDataHub()
	{
		Log.d(TAG, "updateTrackDataHub");
		if (trackDataHub != null)
		{
			trackDataHub.forceUpdateLocation();
		}
	}

	/**
	 * Updates the map by either zooming to the requested waypoint or showing the
	 * track.
	 * 
	 * @param track
	 *          the track
	 */
	private void updateMap(Track track)
	{
		Log.d(TAG, "updateMap");
		if (track.getId() == waypointTrackId)
		{
			// Show the waypoint
			showWaypoint(waypointId);

			waypointTrackId = -1L;
			waypointId = -1L;
		}
		else
		{
			// Show the track
			showTrack(track);
		}
	}

	/**
	 * Returns true if the location is visible.
	 * 
	 * @param location
	 *          the location
	 */
	private boolean isVisible(Location location)
	{
		Log.d(TAG, "isVisible");
		if (location == null || mMap == null)
		{
			return false;
		}
		GeoPointYf mapCenter = mMap.getMapCenter();
		int latitudeSpan = mMap.getLatitudeSpan();
		int longitudeSpan = mMap.getLongitudeSpan();

		/*
		 * The bottom of the mapView is obscured by the zoom controls, subtract its
		 * height from the visible area.
		 */
		GeoPointYf zoomControlBottom = mMap.fromPixels(0, mMap.getMapView().getHeight());
		GeoPointYf zoomControlTop = mMap.fromPixels(0, mMap.getMapView().getHeight()
				/*- mapView.getZoomControls().getHeight()*/);
		int zoomControlMargin = Math.abs(zoomControlTop.getLatitudeE6() - zoomControlBottom.getLatitudeE6());
		GeoRect geoRect = new GeoRect(mapCenter, latitudeSpan, longitudeSpan);
		geoRect.top += zoomControlMargin;

		GeoPointYf geoPoint = LocationUtils.getGeoPoint(location);
		return geoRect.contains(geoPoint);
	}

	/**
	 * Updates the current location and centers it if necessary.
	 */
	private void updateCurrentLocation()
	{
		Log.d(TAG, "updateCurrentLocation");
		if (mSegmentOverlay == null || mMap == null || currentLocation == null)
		{
			return;
		}

		if (!LocationUtils.isValidLocation(currentLocation))
			return;

		Log.w("updateCurrentLocation", "updateCurrentLocation " + " Latitude = " + currentLocation.getLatitude()
				+ " Longitude = " + currentLocation.getLongitude());
		mSegmentOverlay.setMyLocation(currentLocation);
		mMap.postInvalidate();

		if (keepMyLocationVisible /* && !isVisible(currentLocation) */)
		{
			GeoPointYf geoPoint = LocationUtils.getGeoPoint(currentLocation);
			mMap.animateTo(geoPoint);
			// mapController.setCenter(geoPoint);
			if (zoomToMyLocation)
			{
				// Only zoom in the first time we show the location.
				zoomToMyLocation = false;
				if (mMap.getZoomLevel() < mMap.getMaxZoomLevel())
				{
					mMap.setZoom(mMap.getMaxZoomLevel());
				}
			}
		}
		else
		{
			if (currentLocation == null)
				Log.d("updateCurrentLocation", "currentLocation == null");
		}
	}

	/**
	 * Shows the track.
	 * 
	 * @param track
	 *          the track
	 */
	private void showTrack(Track track)
	{
		Log.d(TAG, "showTrack");
		if (mMap == null || track == null || track.getNumberOfPoints() < 2)
		{
			return;
		}

		TripStatistics tripStatistics = track.getStatistics();
		int bottom = tripStatistics.getBottom();
		int left = tripStatistics.getLeft();
		int latitudeSpanE6 = tripStatistics.getTop() - bottom;
		int longitudeSpanE6 = tripStatistics.getRight() - left;
		if (latitudeSpanE6 > 0 && latitudeSpanE6 < 180E6 && longitudeSpanE6 > 0 && longitudeSpanE6 < 360E6)
		{
			keepMyLocationVisible = false;
			GeoPointYf center = new GeoPointYf(bottom + latitudeSpanE6 / 2, left + longitudeSpanE6 / 2);
			if (LocationUtils.isValidGeoPoint(center))
			{
				mMap.setCenter(center);
				mMap.zoomToSpan(latitudeSpanE6, longitudeSpanE6);
			}
		}
	}

	private void HideUI()
	{
		Log.d(TAG, "HideUI");
		mMap.getActivity().findViewById(R.id.activitySelect).setVisibility(View.GONE);
		mMap.getActivity().findViewById(R.id.TableLayout0).setVisibility(View.GONE);
		mMap.getActivity().findViewById(R.id.TableLayout1).setVisibility(View.GONE);
	}

	private void InitUI()
	{
		Log.d(TAG, "InitUI");
		InitButton();
		InitSpinner();
		InitButtonGroup();
		setLocationUnknown();
		// HideUI();
	}

	private void InitButton()
	{
		myPositionBtn = (ImageButton) mMap.getActivity().findViewById(R.id.MyPosButton);
		myPositionBtn.setOnClickListener(this);

		startRecordBtn = (ImageButton) mMap.getActivity().findViewById(R.id.StartRecButton);
		startRecordBtn.setOnClickListener(this);

		fullScreenMapBtn = (ImageButton) mMap.getActivity().findViewById(R.id.mapFullScreen);
		fullScreenMapBtn.setOnClickListener(this);
	}

	private void InitSpinner()
	{

		String sactivity_walking = mMap.getActivity().getResources().getString(R.string.activity_walking);
		String sactivity_running = mMap.getActivity().getResources().getString(R.string.activity_running);
		String sactivity_cycling = mMap.getActivity().getResources().getString(R.string.activity_cycling);
		String sactivity_hiking = mMap.getActivity().getResources().getString(R.string.activity_hiking);
		String sactivity_golf = mMap.getActivity().getResources().getString(R.string.activity_golf);
		String sactivity_mountainBiking = mMap.getActivity().getResources().getString(R.string.activity_mountainBiking);

		int typeNum = mActivityType.getNum();
		String activityType[] = new String[typeNum];

		for (EActivityType s : EActivityType.values())
		{
			switch (s)
			{
				case activity_walking:
				{
					activityType[s.index()] = sactivity_walking;
					break;
				}
				case activity_running:
				{
					activityType[s.index()] = sactivity_running;
					break;
				}
				case activity_cycling:
				{
					activityType[s.index()] = sactivity_cycling;
					break;
				}
				case activity_hiking:
				{
					activityType[s.index()] = sactivity_hiking;
					break;
				}
				case activity_golf:
				{
					activityType[s.index()] = sactivity_golf;
					break;
				}
				case activity_mountainBiking:
				{
					activityType[s.index()] = sactivity_mountainBiking;
					break;
				}
				default:
					break;
			}
		}

		activitySelect = (Spinner) mMap.getActivity().findViewById(R.id.activitySelect);

		ArrayAdapter<String> adapter = new ArrayAdapter<String>(mMap.getActivity(), android.R.layout.simple_spinner_item, activityType);
		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		activitySelect.setAdapter(adapter);
		activitySelect.setSelection(0);
		activitySelect.setOnItemSelectedListener(new OnItemSelectedListener()
		{

			public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
			{
				// TODO Auto-generated method stub
				int pos = activitySelect.getSelectedItemPosition();
				SetButtonGroupType(EActivityType.getValueFromInt(pos));
			}

			public void onNothingSelected(AdapterView<?> arg)
			{
				// TODO Auto-generated method stub

			}
		});
	}

	private void InitButtonGroup()
	{
		for (int i = 0; i < KBUTTONNUMBER; i++)
			mInfoCtrlGroup[i] = new InfoCtrlGroup(mMap.getActivity());

		mInfoCtrlGroup[0].initFromRes(R.id.TableLayout_Button1, R.id.TableLayout_TitleText1);
		mInfoCtrlGroup[1].initFromRes(R.id.TableLayout_Button2, R.id.TableLayout_TitleText2);
		mInfoCtrlGroup[2].initFromRes(R.id.TableLayout_Button3, R.id.TableLayout_TitleText3);
		mInfoCtrlGroup[3].initFromRes(R.id.TableLayout_Button4, R.id.TableLayout_TitleText4);

		// to do:use preference
		SetButtonGroupType(EActivityType.activity_walking);

	}

	private void SetButtonGroupType(EActivityType activityType)
	{
		switch (activityType)
		{
			case activity_walking:
			{
				InfoCtrlGroup.SetButtonInfoType(mInfoCtrlGroup[0], EActivityStats.eSpeed, metricUnits);
				InfoCtrlGroup.SetButtonInfoType(mInfoCtrlGroup[1], EActivityStats.eDistance, metricUnits);
				InfoCtrlGroup.SetButtonInfoType(mInfoCtrlGroup[2], EActivityStats.eElevation, metricUnits);
				InfoCtrlGroup.SetButtonInfoType(mInfoCtrlGroup[3], EActivityStats.eTotalTime, metricUnits);
				break;
			}
			case activity_running:
			{
				InfoCtrlGroup.SetButtonInfoType(mInfoCtrlGroup[0], EActivityStats.eSpeed, metricUnits);
				InfoCtrlGroup.SetButtonInfoType(mInfoCtrlGroup[1], EActivityStats.eDistance, metricUnits);
				InfoCtrlGroup.SetButtonInfoType(mInfoCtrlGroup[2], EActivityStats.eAvgSpeed, metricUnits);
				InfoCtrlGroup.SetButtonInfoType(mInfoCtrlGroup[3], EActivityStats.eTotalTime, metricUnits);
				break;
			}
			case activity_cycling:
			{
				break;
			}
			case activity_hiking:
			{
				InfoCtrlGroup.SetButtonInfoType(mInfoCtrlGroup[0], EActivityStats.eElevation, metricUnits);
				InfoCtrlGroup.SetButtonInfoType(mInfoCtrlGroup[1], EActivityStats.eElevationGain, metricUnits);
				InfoCtrlGroup.SetButtonInfoType(mInfoCtrlGroup[2], EActivityStats.eDistance, metricUnits);
				InfoCtrlGroup.SetButtonInfoType(mInfoCtrlGroup[3], EActivityStats.eTotalTime, metricUnits);
				break;
			}
			case activity_golf:
			{
				break;
			}
			case activity_mountainBiking:
			{
				break;
			}
			default:
				break;
		}

		UpdateInfoUnit();
	}

	private void UpdateInfoGroupData(InfoCtrlGroup aCtrl, Location aLoc, TripStatistics stats)
	{
		if (aCtrl == null)
			return;

		switch (aCtrl.GetStats())
		{
			case eStartTime:
			case eMovingTime:
			case eAvgMovingSpeed:
			case eMaxSpeed:
			case eMinElevation:
			case eMaxElevation:
			case eMinGrade:
			case eMaxGrade:
			{
				break;
			}

			case eSpeed:
			{
				if (aLoc != null)
				{
					float speed = aLoc.getSpeed();
					statsUtilities.setSpeed(aCtrl.mButtonResId, aLoc.getSpeed() * UnitConversions.MS_TO_KMH);
				}
				break;
			}
			case eAvgSpeed:
			{
				if (stats != null)
				{
					statsUtilities.setSpeed(aCtrl.mButtonResId, stats.getAverageSpeed() * UnitConversions.MS_TO_KMH);
				}
				break;
			}
			case eTotalTime:
			{
				if (stats != null)
				{
					statsUtilities.setTime(aCtrl.mButtonResId, stats.getTotalTime());
				}
				break;
			}
			case eDistance:
			{
				if (stats != null)
				{
					statsUtilities.setDistance(aCtrl.mButtonResId, stats.getTotalDistance() * UnitConversions.M_TO_KM);
				}
				break;
			}
			case eElevation:
			{
				if (aLoc != null)
				{
					statsUtilities.setAltitude(aCtrl.mButtonResId, aLoc.getAltitude());
				}
				break;
			}
			case eElevationGain:
			{

				break;
			}
		}
	}

	private void UpdateInfoGroupUnit(InfoCtrlGroup aCtrl)
	{

		if (aCtrl == null)
			return;

		switch (aCtrl.GetStats())
		{
			case eStartTime:
			case eMovingTime:
			case eAvgMovingSpeed:
			case eMaxSpeed:
			{
				statsUtilities.setSpeedUnitAndName(aCtrl.mtextTitleResId, R.string.stat_max_speed, R.string.stat_fastest_pace);
				break;
			}
			case eMinElevation:
			case eMaxElevation:
			case eMinGrade:
			case eMaxGrade:
			{
				break;
			}

			case eSpeed:
			{
				statsUtilities.setSpeedUnitAndName(aCtrl.mtextTitleResId, R.string.stat_speed, R.string.stat_pace);
				break;
			}
			case eAvgSpeed:
			{
				statsUtilities.setSpeedUnitAndName(aCtrl.mtextTitleResId, R.string.stat_average_speed, R.string.stat_average_pace);
				break;
			}
			case eTotalTime:
			{
				// aCtrl.infoTextUnitGroup.setText("");
				break;
			}
			case eDistance:
			{
				// statsUtilities.setDistanceUnits(aCtrl.mtextUnitResId);
				break;
			}
			case eElevation:
			{
				// statsUtilities.setAltitudeUnits(aCtrl.mtextUnitResId);
				break;
			}
			case eElevationGain:
			{
				// statsUtilities.setAltitudeUnits(aCtrl.mtextUnitResId);
				break;
			}
			case eNone:
				break;
		}
	}

	private void UpdateLocationInfo(Location location)
	{
		for (int i = 0; i < KBUTTONNUMBER; i++)
			UpdateInfoGroupData(mInfoCtrlGroup[i], location, null);
	}

	private void UpdateTrackInfo(Track track)

	{
		for (int i = 0; i < KBUTTONNUMBER; i++)
			UpdateInfoGroupData(mInfoCtrlGroup[i], null, track.getStatistics());
	}

	private void UpdateInfoUnit()
	{
		for (int i = 0; i < KBUTTONNUMBER; i++)
			UpdateInfoGroupUnit(mInfoCtrlGroup[i]);
	}

	// A runnable to update the total time field.
	private final Runnable updateTotalTime = new Runnable()
	{
		public void run()
		{
			UpdateUI();
		}
	};

	private void UpdateUI()
	{
		// update play/pause button status
		setTheGoAndStopBtn();

		UpdateGpsAcc();

		// auto follow
		if (isAutoFollowing)
		{
			showMyLocation();
		}

		if (isRecording())
		{
			for (int i = 0; i < KBUTTONNUMBER; i++)
			{
				if (mInfoCtrlGroup[i].GetStats() == EActivityStats.eTotalTime)
				{
					statsUtilities.setTime(mInfoCtrlGroup[i].mButtonResId, System.currentTimeMillis() - startTime);
				}
			}
		}
	}

	private class UiUpdateThread extends Thread
	{

		@Override
		public void run()
		{
			Log.d(TAG, "UI update thread started");
			// while (PreferencesUtils.getRecordingTrackId(mMap.getActivity()) != -1L)
			while (true)
			{
				mMap.getActivity().runOnUiThread(updateTotalTime);
				try
				{
					Thread.sleep(1000L);
				}
				catch (InterruptedException e)
				{
					Log.d(TAG, "UI update thread caught exception", e);
					break;
				}
			}
			Log.d(TAG, "UI update thread finished");
		}
	}

	private void setFullMap(boolean fullScreen)
	{
		if (fullScreen)
		{
			mMap.getActivity().findViewById(R.id.activitySelect).setVisibility(View.GONE);
			mMap.getActivity().findViewById(R.id.TableLayout0).setVisibility(View.GONE);
			mMap.getActivity().findViewById(R.id.TableLayout1).setVisibility(View.GONE);
			mState = EMapViewState.MapViewMapFullScreen;
		}
		else
		{
			mMap.getActivity().findViewById(R.id.activitySelect).setVisibility(View.VISIBLE);
			mMap.getActivity().findViewById(R.id.TableLayout0).setVisibility(View.VISIBLE);
			mMap.getActivity().findViewById(R.id.TableLayout1).setVisibility(View.VISIBLE);
			mState = EMapViewState.MapViewNormal;
		}
	}

	private void cancelAutoFollow()
	{
		if (isAutoFollowing == true)
		{
			isAutoFollowing = false;
			Toast.makeText(mMap.getActivity(), R.string.no_following, Toast.LENGTH_LONG).show();
		}
	}

	private void startAutoFollow()
	{
		if (showMyLocation() == true)
		{
			isAutoFollowing = true;
			Toast.makeText(mMap.getActivity(), R.string.now_following, Toast.LENGTH_LONG).show();
		}
	}

	private void cancelAutoFollowSilent()
	{
		if (isAutoFollowing == true)
		{
			isAutoFollowing = false;
		}
	}

	private void startAutoFollowSilent()
	{
		if (showMyLocation() == true)
		{
			isAutoFollowing = true;
		}
	}

	public void setTheGoAndStopBtn()
	{
		MainTabActivity mainApp = MainTabActivity.getInstance();
		if (mainApp != null)
		{
			IsRecoding = mainApp.isRecording();
			if (IsRecoding)
			{
				startRecordBtn.setImageDrawable(mMap.getActivity().getResources().getDrawable(R.drawable.menu_stop));
			}
			else
			{
				startRecordBtn.setImageDrawable(mMap.getActivity().getResources().getDrawable(R.drawable.menu_record));
			}
		}

	}
	
	public void draw(Canvas canvas)
	{
		mSegmentOverlay.draw(canvas, mMap);
	}

}
